package at.technikum_wien.embsys.aat.PriscillaCore.component;

import java.util.Calendar;
import java.util.List;

import at.technikum_wien.embsys.aat.PriscillaCore.enums.EnoceanComponentType;
import at.technikum_wien.embsys.aat.PriscillaCore.telegram.Telegram;



/**
 * 
 * EEP_07_13_04 component class - environmental applications
 *
 * Type: environmental applications - time and day exchange
 * Weekday
 * Hour/minute/second
 * Source: RTC, GPS (or equivalent)
 * 
 * @author B. Aigner
 * 
 */
public class EEP_07_13_04 implements EnoceanComponent {
	
	Telegram telegram;
	List<?> eventListener;
	
	Calendar date;
	
	boolean sourceRTC;
	boolean sourceOther;
	
	int identifier;
	
	EnoceanComponentType enoceanComponentType = EnoceanComponentType.EEP_07_13_04;
	boolean telegramCreation = false;

	/**
	 * This constructor is used for received packets.
	 * It will parse the telegram and set the corresponding members.
	 * 
	 * @param telegram Received Telegram
	 * @param listener Event subscriber list
	 */
	public EEP_07_13_04(Telegram telegram, List<?> listener) {
		super();
		this.telegram = telegram;
		this.eventListener = listener;
		

		identifier = (telegram.getData0() & 0xF0) / 16;
		
		switch((telegram.getData3() & 0xE0) / 32) {
		case 1:
			date.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
			break;
		case 2:
			date.set(Calendar.DAY_OF_WEEK, Calendar.TUESDAY);
			break;
		case 3:
			date.set(Calendar.DAY_OF_WEEK, Calendar.WEDNESDAY);
			break;
		case 4:
			date.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
			break;
		case 5:
			date.set(Calendar.DAY_OF_WEEK, Calendar.FRIDAY);
			break;
		case 6:
			date.set(Calendar.DAY_OF_WEEK, Calendar.SATURDAY);
			break;
		case 7:
			date.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
			break;
		}
		
		if(checkBit(telegram.getData0(),2)) {
			//AM/PM 12h
			date.set(Calendar.HOUR, telegram.getData3() & 0x1F);
			if(checkBit(telegram.getData0(),1)) {
				date.set(Calendar.AM_PM, Calendar.PM);
			} else {
				date.set(Calendar.AM_PM, Calendar.AM);
			}
		} else {
			//24h
			date.set(Calendar.HOUR_OF_DAY, telegram.getData3() & 0x1F);
		}
		
		
		date.set(Calendar.MINUTE, telegram.getData2() & 0x3F);
		date.set(Calendar.SECOND, telegram.getData1() & 0x3F);

		if(checkBit(telegram.getData0(), 0)) {
			sourceOther = true;
			sourceRTC = false;
		} else {
			sourceOther = false;
			sourceRTC = true;
		}

		fireFrame();
	}

	/**
	 * Fire all associated events
	 */
	void fireFrame() {
		//TODO: TimeEvent
		/*
		Iterator listen = eventListener.iterator();
		final FrameEventFan frameEventFan = new FrameEventFan(this, telegram.getDeviceID(), this.fanspeed, this.fantype );
		while (listen.hasNext()) {
			((LinkListener) listen.next()).frameReceived_Fan(frameEventFan);
		}
		*/
	}

	/**
	 * Void constructor. It is used to create a new telegram for sending
	 */
	public EEP_07_13_04() {
		super();
		telegram = new Telegram();
		telegramCreation = true;
	}

	/**
	 * This constructor is used to create a telegram (the device id is given as parameter)
	 * @param deviceID Full device ID
	 */
	public EEP_07_13_04(String deviceID) {
		super();
		telegram = new Telegram();
		telegram.setDeviceID(deviceID);
		telegramCreation = true;
	}
	
	boolean checkBit(int value, int bitNr) {
		return (value & (1 << bitNr)) != 0;
	}
	
	int setBit(int value, int bitNr) {
		return value | (1<<bitNr);
	}
	
	int clrBit(int value, int bitNr) {
		return value & (~(1<<bitNr));
	}
	
	@Override
	public String getDeviceID() {
		return telegram.getDeviceID();
	}
	@Override
	public String getORG() {
		return telegram.getxORG();
	}
	@Override
	public EnoceanComponentType getType() {
		return enoceanComponentType;
	}

	@Override
	public void setDeviceID(String deviceID) {
		telegram.setDeviceID(deviceID);
	}

	/**
	 * Creates a new telegram from the component
	 * 
	 * This method creates a new telegram (if the component is initialized with
	 * a constructor without a telegram as parameter) or returns the saved telegram.
	 * The new telegram is built up from the members in this class (in this case environmental data). 
	 * @return Created telegram
	 */
	public Telegram getTelegram() {
		if(telegramCreation == true) {
			telegram.setData3(date.get(Calendar.HOUR_OF_DAY));
			
			switch(date.get(Calendar.DAY_OF_WEEK)) {
			case Calendar.MONDAY:
				telegram.setData3(telegram.getData3() + 1 * 32);
				break;
			case Calendar.TUESDAY:
				telegram.setData3(telegram.getData3() + 2 * 32);
				break;
			case Calendar.WEDNESDAY:
				telegram.setData3(telegram.getData3() + 3 * 32);
				break;
			case Calendar.THURSDAY:
				telegram.setData3(telegram.getData3() + 4 * 32);
				break;
			case Calendar.FRIDAY:
				telegram.setData3(telegram.getData3() + 5 * 32);
				break;
			case Calendar.SATURDAY:
				telegram.setData3(telegram.getData3() + 6 * 32);
				break;
			case Calendar.SUNDAY:
				telegram.setData3(telegram.getData3() + 7 * 32);
				break;
			}
			
			telegram.setData2(date.get(Calendar.MINUTE));
			telegram.setData1(date.get(Calendar.SECOND));
			
			telegram.setData0(identifier * 16);
			
			telegram.setData0(clrBit(telegram.getData0(),2));

			if(date.get(Calendar.AM_PM) == Calendar.AM) {
				telegram.setData0(clrBit(telegram.getData0(),1));
			} else {
				telegram.setData0(setBit(telegram.getData0(),1));
			}

			if(sourceRTC == true) {
				telegram.setData0(clrBit(telegram.getData0(),0));
			} else {
				telegram.setData0(setBit(telegram.getData0(),0));
			}
		}
		return telegram;
	}

	public Calendar getDate() {
		return date;
	}

	public boolean isSourceRTC() {
		return sourceRTC;
	}

	public boolean isSourceOther() {
		return sourceOther;
	}

	public int getIdentifier() {
		return identifier;
	}
	
	public void setSourceRTC(boolean sourceRTC) {
		this.sourceRTC = sourceRTC;
		this.sourceOther = !sourceRTC;
	}

	public void setSourceOther(boolean sourceOther) {
		this.sourceOther = sourceOther;
		this.sourceRTC = !sourceOther;
	}

	public void setIdentifier(int identifier) {
		if(identifier > 15) return;
		this.identifier = identifier;
	}

	public void setDate(Calendar date) {
		this.date = date;
	}
}
